import csv
import os
import threading
import socket
import pandas as pd
from user import User
import queue
from tools import MessageTool as MT


class Server:
    def __init__(self, homedir, csvPath):
        # 记录用户和密码的对应关系
        self.password_dic = {}
        self.usersDic = {}  # 存储所有用户的字典
        self.userNumberQueue = queue.Queue()  # 用队列管理用户编号
        for i in range(1, 256):
            self.userNumberQueue.put(i)
        self.msgQueue = queue.Queue()  # 用户消息队列
        self.csvPath = csvPath
        if not os.path.exists(csvPath):
            f=open(csvPath,'w')
            f.close()
        self.homedir = homedir
        with open(self.csvPath, 'r') as f:
            reader = csv.reader(f)
            for row in reader:
                if len(row) <2:
                    continue
                self.password_dic[row[0]] = int(row[1])
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.bind(('0.0.0.0', 12345))
        self.s.listen(10)
        # 控制csv文件的写入
        self.csvWriteEvent = threading.Event()
        # 防止写入时字典被修改
        self.dicWriteLock = threading.Lock()
        # 防止多个用户同时在公共文件夹上传时，上传了同名文件，同时写入一个文件
        self.fileWirteLock = threading.Lock()

    def saver(self):
        while True:
            # 等待写入
            self.csvWriteEvent.wait()
            # 加锁
            self.dicWriteLock.acquire()
            sr = pd.Series(self.password_dic)
            sr.to_csv(self.csvPath)
            self.csvWriteEvent.clear()
            self.dicWriteLock.release()

    def broadcast(self, msg):
        '''
        广播函数
        :param msg: 要发送的信息
        :return:
        '''
        for k, v in self.usersDic.items():
            if not k==msg[4]:
                v.msgQueue.put(msg)

    def msgProcesser(self):
        '''
        用户消息处理线程
        :return:
        '''
        while True:
            msg = self.msgQueue.get()
            '''
            msg[0]=-3 代表新用户登录失败, msg[1] 为用户序号
            msg[0]=-2 代表新用户登录成功, msg[1]为用户序号, msg[2]为要广播的消息
            msg[0]=-1 代表用户退出事件, msg[1] 为用户序号, msg[2]为要广播的消息
            0 代表群发事件 msg[1] 为要发送的消息
            其他正常代表转发 msg[1] 为要发的消息
            '''
            if msg[0] == -3:  # 删除并将用户序号放回分配队列
                self.usersDic.pop(msg[1])
                self.userNumberQueue.put(msg[1])
            elif msg[0] == -2:  # 向用户发送在线列表并广播用户添加信息
                # 0 代表广播
                self.usersDic[msg[1]].msgQueue.put(User.packing(MT.S_USER_LIST, MT.APPEND, 0, message=b'all'))
                for _, v in self.usersDic.items():
                    if msg[1] is not v.userNumber:
                        self.usersDic[msg[1]].msgQueue.put(
                            User.packing(MT.S_USER_LIST, MT.APPEND, v.userNumber, message=v.ID.encode('utf-8')))
                self.broadcast(msg[2])
            elif msg[0] == -1:  # 删除并广播用户删除信息，同时将用户序号放回分配队列
                self.usersDic.pop(msg[1])
                self.broadcast(msg[2])
                self.userNumberQueue.put(msg[1])
            elif msg[0] == 0:  # 广播消息
                self.broadcast(msg[1])
            elif 0 < msg[0] < 256:
                try:
                    self.usersDic[msg[0]].msgQueue.put(msg[1])
                except:
                    pass

    def listen(self):
        while True:
            sock, addr = self.s.accept()
            print('收到一个新连接：', addr)
            if self.userNumberQueue.empty():
                print('已达到最大服务数量')
                sock.close()
                continue
            userNumber = self.userNumberQueue.get()
            user = User(sock, addr, self.password_dic, self.homedir, self.csvWriteEvent, self.dicWriteLock,
                        self.fileWirteLock, self.msgQueue, userNumber)
            self.usersDic[userNumber] = user
            threading.Thread(target=user.login).start()

    def run(self):
        threading.Thread(target=self.saver).start()
        threading.Thread(target=self.listen).start()
        threading.Thread(target=self.msgProcesser).start()


if __name__ == '__main__':
    csvPath = './res/password.csv'
    homedir = './testfiles'
    server = Server(homedir, csvPath)
    server.run()
